四旋翼动力学和仿真翻译(Quadcopter Dynamics and Simulation)

您所在的位置:网站首页 旋翼无人机 英语 四旋翼动力学和仿真翻译(Quadcopter Dynamics and Simulation)

四旋翼动力学和仿真翻译(Quadcopter Dynamics and Simulation)

2024-07-12 15:43| 来源: 网络整理| 查看: 265

本文翻译自Andrew Gibiansky的同名文章,该文献介绍了四旋翼的动力学模型和Matlab仿真的具体实现,对四旋翼入门非常有好处。原文如下 http://andrew.gibiansky.com/blog/physics/quadcopter-dynamics/

由于Neo已经于2014年对该文章前半部分进行了翻译(从Introduction到Physics),因此本文将从Simulation部分开始翻译。已经翻译部分如下

http://share.weiyun.com/9c1869b64a76fea67047adedc2f2eb14

本文的翻译工作由BUAA飞机总体设计HT11课程小组成员完成,具体分工如下:

孟德超 Page6~8,simulation和Control(PD control之前) 李志宇 page 8~11 PD control 刘倚铭 page11-14 PID control, 唐既尧 关智元 周震 page15 ~18 Automatic PID Tuning & conclusion

仿真控制PD控制PID控制自动PID调节结论

仿真

既然我们已经建立了描述此系统动力学的完整的方程组,因此我们可以搭建一个仿真环境,从而我们可以在仿真环境中实时观察到我们的变量和控制器的变化。尽管有很多更先进的方法可以选择,我们仍可以通过欧拉方程建立差分方程,进而模拟不同系统状态的发展情况。在Matlab中,这个仿真环境的部分代码如下

% Simulation times, in seconds. start_time = 0; end_time = 10; dt = 0.005; times = start_time:dt:end_time; % Number of points in the simulation. N = numel(times); % Initial simulation state. x = [0; 0; 10]; xdot = zeros(3, 1); theta = zeros(3, 1); % Simulate some disturbance in the angular velocity. % The magnitude of the deviation is in radians / second. deviation = 100; thetadot = deg2rad(2 * deviation * rand(3, 1) - deviation); % Step through the simulation, updating the state. for t = times % Take input from our controller. i = input(t); omega = thetadot2omega(thetadot, theta); % Compute linear and angular accelerations. a = acceleration(i, theta, xdot, m, g, k, kd); omegadot = angular_acceleration(i, omega, I, L, b, k); omega = omega + dt * omegadot; thetadot = omega2thetadot(omega, theta); theta = theta + dt * thetadot; xdot = xdot + dt * a; x = x + dt * xdot; end

我们还需要计算力和力矩的函数

% Compute thrust given current inputs and thrust coefficient. function T = thrust(inputs, k) % Inputs are values for ${\omega_i}^2$ T = [0; 0; k * sum(inputs)]; end % Compute torques, given current inputs, length, drag coefficient, and thrust coefficient. function tau = torques(inputs, L, b, k) % Inputs are values for ${\omega_i}^2$ tau = [ L * k * (inputs(1) - inputs(3)) L * k * (inputs(2) - inputs(4)) b * (inputs(1) - inputs(2) + inputs(3) - inputs(4)) ]; end function a = acceleration(inputs, angles, xdot, m, g, k, kd) gravity = [0; 0; -g]; R = rotation(angles); T = R * thrust(inputs, k); Fd = -kd * xdot; a = gravity + 1 / m * T + Fd; end function omegadot = angular_acceleration(inputs, omega, I, L, b, k) tau = torques(inputs, L, b, k); omegaddot = inv(I) * (tau - cross(omega, I * omega)); end

现在我们还需要的是一些物理常数,一个计算旋转矩阵的函数,以及一个把 ω 转化为滚转,俯仰和偏航(roll,pitch,yaw)角的微分的函数以及它的反函数。这些函数没有写明。我们可以绘制一个三维坐标下的可视化的四旋翼并且让它在仿真运行时不停更新自己的位置。 注意:虽然原文没有写明该部分的实现,但是考虑到matlab仿真并非易事,此处给出一个译者实现的源代码链接:https://github.com/silverbulletmdc/quadcopter_simulator_in_matlab 这里写图片描述 四旋翼仿真。每个螺旋桨上的长条粗略的表示其相对升力大小。

控制

推导四旋翼的数学模型是为了协助我们为真实的四旋翼开发一个控制器。系统的输入包括四个电机的角速度,因为我们可以通过控制电压输出值直接控制角速度。注意在我们的简化模型里,我们仅仅采用角速度的平方 ωi2 而不使用角速度本身。为了计数简单,我们引入输入变量 γi=ωi2 。这是因为既然我们可以直接控置 ωi ,那我们也可以直接控制 γi 。这样,我们就可以将我们的系统描述为状态空间中的一阶微分方程。令 x1 为四旋翼在空间中的位置, x2 为四旋翼的线速度, x3 为四旋翼的滚转、俯仰、偏航角, x4 为角速度。(注意这些变量都是三维向量)。有了这些变量,我们可以写出状态空间方程如下:

x1˙x2˙x3˙x4˙=x2=⎡⎣⎢00−g⎤⎦⎥+1mRTB+1mFD=⎡⎣⎢1000cϕ−sϕ−sθcθsϕcθcϕ⎤⎦⎥−1x4=⎡⎣⎢⎢τϕIxx−1τθIyy−1τψIzz−1⎤⎦⎥⎥−⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢Iyy−IzzIxxωyωzIzz−IxxIyyωxωzIxx−IyyIzzωxωy⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥ 注意我们的输入并没有直接作用在这些方程上。 然而,正如我们所看到的,我们可以选择 τ 和 T 的值,然后解出γi的值。

PD控制

为了控制四轴飞行器,我们将使用一个PD控制器。给期望的轨迹与观测到的轨迹之间的误差和该误差的导数各加一个比例环节。我们的四轴飞行器将只有一个陀螺,所以在控制器中只能使用角度导数 ϕ˙,θ˙,ϕ˙ 来控制飞行器;这些测量值提供误差的导数,其积分将为我们提供实际的误差。我们想让直升机稳定在一个水平位置,所以我们期望的速度和角度都是零。扭矩与角速度有关: τ=Iθ¨ ,所以我们将力矩设置成与控制器的输出成正比,即 τ=Iu(t) 。因此

⎡⎣⎢τϕτθτψ⎤⎦⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢−Ixx(Kdϕ˙+Kp∫T0ϕ˙dt)−Iyy(Kdθ˙+Kp∫T0θ˙dt)−Izz(Kdψ˙+Kp∫T0ψ˙dt)⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥ 我们先前推导出转矩和输入之间的关系,所以我们知道 τB=⎡⎣⎢Lk(γ1−γ3)Lk(γ2−γ4)b(γ1−γ2+γ3−γ4)⎤⎦⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢−Ixx(Kdϕ˙+Kp∫T0ϕ˙dt)−Iyy(Kdθ˙+Kp∫T0θ˙dt)−Izz(Kdψ˙+Kp∫T0ψ˙dt)⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥ 这给了我们一组包含四个未知数的三个方程。我们可以加一个限制条件,即输入必须保持四轴飞行器悬浮在空中: T=mg 注意,这个方程忽视了一个事实:推力没有直接给出。这会限制我们的控制器的适用性,但不会对小的稳定偏离造成重大问题。如果我们有办法确定当前角度的准确值,我们就可以补偿这个不足。如果我们的陀螺足够精确,我们可以用从陀螺采集到的数据进行积分,得到角度θ和φ。在这种情况下,我们可以把mg投影到z轴上来计算保持四旋翼悬浮所需的推力。我们发现 Tproj=mgcosθcosϕ

因此,有精密的角度测量,我们就可以要求推力等于

T=mgcosθcosϕ 在这种情况下,指向正z轴的推力分量就等于mg。我们知道推力正比于输入的加权总和: T=mgcosθcosϕ=k∑γi⟹∑γi=mgkcosθcosϕ 这个额外的约束,我们有一组包含四个未知数 的四个线性方程。我们 可以求解每个未知量 ,并获得以下输入值: γ1γ2γ3γ4=mg4kcosθcosϕ−2beϕIxx+eψIzzkL4bkL=mg4kcosθcosϕ+eψIzz4b−eθIyy2kL=mg4kcosθcosϕ−−2beϕIxx+eψIzzkL4bkL=mg4kcosθcosϕ+eψIzz4b+eθIyy2kL 这是一个PD控制器的完整描述。我们可以使用我们的仿真环境来模拟这个控制器。控制器使角速度和角度为零。 这里写图片描述 左:角速度。右:角位移。θ,φψ是依次为红、绿、蓝色的。 然而,注意角度最终不是完全为零。平均稳态误差(仿真10秒后的误差)大约是 0.3∘ 。 对机械系统使用PD控制器这是一个常见的问题,可以用PID控制器改善,就像我们将在下一节中讨论的那样。 此外,请注意,由于我们只有控制角速度,位置和线速度不收敛于零。然而,z轴上的位置保持不变,因为我们用 “保持四轴飞行器悬浮在空中” 限制了总的垂直推力,没有上升或下降。然而,这只不过是满足好奇心罢了。除了有限的传感器(陀螺仪),对四轴飞行器的线性位置和速度控制我们什么也做不了。虽然在理论上我们可以用角速度计算位置和线速度,但实际中值的噪声太大而完全不可行。因此,我们将限制自己去稳定四轴飞行器角度和角速度(传统上,导航是由人完成,稳定只是为了使对操纵手来说更加简单。) 我们在仿真中实现了这个PD控制。控制器的实现作为一种手段,它被给定一些状态(对应控制器状态,而非系统状态)和传感器输入,必须计算出输入γ我和更新后的状态。下面给出了PD控制的代码。

% Compute system inputs and updated state. % Note that input = [$\gamma_1$, $\ldots$, $\gamma_4$] function [input, state] = pd_controller(state, thetadot) % Controller gains, tuned by hand and intuition. Kd = 4; Kp = 3; % Initialize the integral if necessary. if ~isfield(state, 'integral') params.integral = zeros(3, 1); end % Compute total thrust total = state.m * state.g / state.k / (cos(state.integral(1)) * cos(state.integral(2))); % Compute errors e = Kd * thetadot + Kp * params.integral; % Solve for the inputs, $\gamma_i$ input = error2inputs(params, accels, total); % Update the state params.integral = params.integral + params.dt .* thetadot; end PID控制

PD控制器的优点是简易,且易于实施,但是他们还不足以控制机械系统。特别是在有噪声和干扰的情况下,PD控制器会产生稳态误差。PID控制相对于PD控制,补充了对过程变量的积分比例项。增加的积分项会消除稳态误差,所以一个PID控制器应该能够跟踪我们的轨迹并稳定四旋翼,稳态误差也会显著减小。方程与在PD的情况下给出的相同,但是增加了附加项的误差:

eϕeθeψ=Kdϕ˙+Kp∫T0ϕ˙dt+Ki∫T0∫T0ϕ˙dtdt=Kdθ˙+Kp∫T0θ˙dt+Ki∫T0∫T0θ˙dtdt=Kdψ˙+Kp∫T0ψ˙dt+Ki∫T0∫T0ψ˙dtdt

然而,PID控制也有弊端,通常出现积分饱和的问题。 这里写图片描述 在某些情况下,积分饱和会导致长时间的振荡。在其他情况下,积分饱和最终可能导致系统不稳定,并不会花费更长的时间去达到稳定状态。 如果对过程变量的扰动较大,由于积分项的存在,这个大扰动对时间积分,将成为一个更大的控制信号。然而,即使系统稳定,积分结果仍然很大,从而造成控制器超调。然后可能开始一系列逐渐变弱的振荡,变得不稳定,或者经过相当长的时间达到稳定状态。为了避免这种情况,在接近接近稳定状态之前,我们要禁用积分环节。一旦我们离稳定状态的区域是可控制的,我们将启用积分环节,这样促使系统朝向一个更低的稳态误差。 这里写图片描述

通过恰当的PID控制,在10秒后,误差大约是0.06 °。 我们已经实现了用PID控制仿真,与前面的PD控制器一样,有一个额外的参数需要调整。为了对比控制器的效果,用于所有测试的干扰都是相同的。

% Compute system inputs and updated state. % Note that input = [$\gamma_1$, $\ldots$, $\gamma_4$] function [input, state] = pid_controller(state, thetadot) % Controller gains, tuned by hand and intuition. Kd = 4; Kp = 3; Ki = 5.5; % Initialize the integral if necessary. if ~isfield(state, 'integral') params.integral = zeros(3, 1); params.integral2 = zeros(3, 1); end % Prevent wind-up if max(abs(params.integral2)) > 0.01 params.integral2(:) = 0; end % Compute total thrust total=state.m*state.g / state.k / (cos(state.integral(1))* cos(state.integral(2))); % Compute errors e = Kd * thetadot + Kp * params.integral - Ki * params.integral2; % Solve for the inputs, $\gamma_i$ input = error2inputs(params, accels, total); % Update the state params.integral = params.integral + params.dt .* thetadot; params.integral2 = params.integral2 + params.dt .* params.integral; end 自动PID调节

虽然PID控制有非常大的潜力,但是其控制品质非常依赖于我们给定的增益参数。鉴于增益参数之间的比率和增益参数本身一样重要,手动调节这些参数可能非常困难。在大多数情况下,调节这些参数需要对整个系统运作有充分的理解和把握,并且了解各个PID参数具体的使用条件。之前这些增益参数就是通过手动调节的,通过做模拟仿真的方式,最终选择那些能使系统正常工作的相对合理的值,整个过程中合理的终值可能不止一个,而模拟仿真实际总是伴随着很多潜在的干扰。这种方式显然不是最优的,不仅因为它非常困难而且工作量非常大,也因为最终的终值在最优性上没有任何的保障。

理想层面上,我们总是乐意能够使用一种算法去分析一个系统然后得出这些最优的PID增益参数。有人深入研究了这个问题,然后提出了许多算法。其中一些算法需要对被仿真系统有全面的认识,其中一些只依赖于这个系统的某些特性,比如稳定性和线性。我们来确定PID参数的算法是知名的极值搜索法。

极值搜索法,顾名思义,我们将参数的理想化标准定义为一些向量 θ⃗ =(Kp,Ki,Kd) ,这可以使代价函数 J(θ⃗ ) 达到最小化。在我们的实例中,我们定义一个价值函数用来补偿那些不能忽视的误差以及超时误差。一个候选的代价函数给出如下

J(θ⃗ )=1tf−to∫tft0e(t,θ⃗ )2dt 其中, e(t,θ⃗ ) 是指在带初始干扰的轨迹跟踪误差,初始干扰用向量 θ⃗  表示。设想如果我们能够通过某种方式计算这个代价函数的梯度 ∇J(θ⃗ ) ,那样的话,我们先定义一个参数迭代公式 θ⃗ (k+1)=θ⃗ (k)−α∇J(θ⃗ ) 然后就可以通过反复迭代的方式来提高我们参数的品质了。 上式中 θ⃗ (k) 是经过 k 次迭代后的参数向量, α是步长,它规定了每次迭代后我们要对我们的参数向量做出多大的调整。当 k→∞ 时,代价函数 J(θ⃗ ) 将会趋近于PID取值空间中的一个局部最小值。

留给我们的问题依然是我们如何才能估计 ∇J(θ⃗ ) 呢?答案是通过定义的方式。

∇J(θ⃗ )=(∂∂KpJ(θ⃗ ),∂∂KiJ(θ⃗ ),∂∂KdJ(θ⃗ )). 我们定义了一个 J(θ⃗ ) , 我们已经知道怎么计算 J(θ⃗ ) 了,通过这个,我们就可以得到任何增益参数的导数的数值近似值,只需要通过计算以下公式就行了 ∂∂KJ(θ⃗ )≈J(θ⃗ +δ⋅u^K)−J(θ⃗ −δ⋅u^K)2δ 其中 u^K 是 K 方向的单位向量,当δ趋近于0时,上述近似值变得更好。通过这个近似值,我们就可以最小化我们的代价函数然后得出局部的最佳PID参数。我们可以从一些随机的初始化正权因子开始计算,然后将我们规定好的扰动施加于系统上,通过使用不同PID参数对系统进行模拟来估计 J(θ⃗ ) 的值,之后我们就可以计算梯度了。然后我们使用梯度下降法通过迭代来不断改善我们的增益参数直到得到某种形式的收敛。

然而,梯度下降法实际存在一些问题。首先,虽然通过它我们找到一个局部最小值,但这个值仅仅只能保证是局部最小的——在全局上面可能还存在其他更好的最小值,我们找到的并非是全局最小值。为了避免我们选择的值非最优的这种情况,我们重复了最优化方案很多次,然后选择其中最好的结果。我们的PID参数初始化是随机的,因此每次我们通过最优化方案都会得到一个不同的结果。另外,我们并没有采用固定的扰动然后去求在这种扰动下的最优响应,我们选用的扰动在单步迭代过程中每一步都是随机的,然后使用其响应的平均值去计算代价和梯度。这样就保证了我们的参数是具有通用性的而不是针对某一特定的扰动。除此之外,我们还将单步迭代的步长和扰动数设置为不一致,这是为了增加我们迭代结果的灵敏度。当我们检测到了一个稳定态时迭代就会停止,我们可以对历史代价函数值做一个线性回归统计计算,然后不停迭代直到变化斜率在统计学上趋于0,我们使用了99%的置信区间。

通过我们的四旋翼仿真,我们可以定义一个函数来计算一组给定PID参数的代价。

function J = cost(theta) % Create a controller using the given gains. control = controller('pid', theta(1), theta(2), theta(3)); % Perform a simulation. data = simulate(control); % Compute the integral, $\frac{1}{t_f - t_0} \int_{t_0}^{t_f} e(t)^2 dt$ t0 = 0; tf = 1; J = 1/(tf - t0) * sum(data.theta(data.t >= t0 & data.t


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3